﻿#include "stdafx.h"
#include "Model.h"
#include "MuBmdModel.h"

Model::Model()
{
    m_boundingBoxMax = glm::vec3(0.0f);
    m_boundingBoxMin = glm::vec3(0.0f);
}

Model::~Model()
{
}

bool VertexCmp(BWeightVertexData &v1, BWeightVertexData &v2)
{
	if (abs(v1.position[0] - v2.position[0])<0.000001 &&
		abs(v1.position[1] - v2.position[1])<0.000001 &&
		abs(v1.position[2] - v2.position[2])<0.000001 &&
		abs(v1.texcoord[0] - v2.texcoord[0])<0.000001 &&
		abs(v1.texcoord[1] - v2.texcoord[1])<0.000001 )
	{
		if (*(int*)v1.boneIndex != *(int*)v2.boneIndex)
		{
			_ASSERT(0);
		}
		float d = glm::dot(glm::make_vec3(v1.normal), glm::make_vec3(v2.normal));
		if (d > 0.7) return true;
	}
	return false;
}
//inline float Magnitude(const float x[3])
//{
//	return (sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]));
//}
//
//inline void Normalize(float x[3])
//{
//	float invMag = 1 / Magnitude(x);
//	x[0] *= invMag;
//	x[1] *= invMag;
//	x[2] *= invMag;
//}

bool Model::Load(char *filePath, TextureManager *texMgr)
{
	auto dot = strrchr(filePath, '.');
	char ext[8];
	strcpy(ext, dot);
	strlwr(ext);
	int fcmp = strcmp(ext, ".bmd");
	if (!fcmp)
	{
		MuBmdModel bmd;
		if (bmd.Load(filePath))
		{
			m_animationData = bmd.m_animData;
			if (bmd.m_meshs.size())
			{
				SetMesh(bmd.m_meshs, texMgr);
			}
			return true;
		}
	}
	return false;
}

void Model::SetMesh(std::vector<BWeightMeshData> &meshs, TextureManager *texMgr)
{
	Animate anim(&m_animationData);
	glm::mat4 *transMat = nullptr;
	int numMat = anim.GetBoneTransformMatrixs(&transMat);
	bool fFirstVertex=true;
	auto &subMeshs = m_vaoMesh.m_subMeshs;
	std::vector<BWeightVertexData> vertexs;
	subMeshs.resize(meshs.size());
	m_materials.resize(meshs.size());
	unsigned int begin = 0;
	std::vector<unsigned int> indices;
	std::set<int> normalSet;
	unsigned int idxCount = 0;
	for (int i = 0; i<meshs.size(); i++)
	{
		auto &mesh = subMeshs[i];
		mesh.vertElementLocation = idxCount;
		auto c = meshs[i].vertexs.size();
		mesh.vertElementCount = c;
		idxCount += c;
		mesh.mode = GL_TRIANGLES;
		auto tex = texMgr->GetTexture(meshs[i].textureName.c_str());
		if (!tex)
		{
			OutputDebugString(" Model.SetMesh LoadTexture err:");
			OutputDebugString(meshs[i].textureName.c_str());
		}
		m_materials[i].m_textures.push_back(tex);
	}
	indices.reserve(idxCount);
	for (int i = 0; i < subMeshs.size(); i++)
	{
		begin = vertexs.size(); //搜索重复的顶点的起始位置位于每个mesh的开头
		auto &mesh = meshs[i];

		for (auto &v : mesh.vertexs)
		{
			glm::vec4 v4(transMat[v.boneIndex[0]] * glm::vec4(glm::make_vec3(v.position), 1.0f));
			if (fFirstVertex)
			{
				fFirstVertex = false;
				for (int i = 0; i < 3; i++)
				{
					m_boundingBoxMax[i] = m_boundingBoxMin[i] = v4[i];
				}
			}
			else
			{
				float f1;
				for (int j = 0; j<3; j++)
				{
					f1 = v4[j];
					if (m_boundingBoxMin[j]>f1) m_boundingBoxMin[j] = f1;
					if (m_boundingBoxMax[j]<f1) m_boundingBoxMax[j] = f1;
				}
			}
			unsigned int index;
			int flag = -1;
			unsigned int currCount = vertexs.size();
			for (index = begin; index<currCount; index++) //遍历现有顶点数组
			{
				if (VertexCmp(vertexs[index], v)) //如果读入的顶点已经存在则记录该顶点index
				{
					flag = index;
					normalSet.insert(index); //记录需要重新计算法线的顶点
					vertexs[index].normal[0] += v.normal[0];
					vertexs[index].normal[1] += v.normal[1];
					vertexs[index].normal[2] += v.normal[2];
					//Add(vertexs[index].normal, v.normal); //法线相加
					break;
				}
			}
			indices.push_back(index);	//记录新顶点的index
			if (flag == -1)
			{
				vertexs.push_back(v);	//如果读入的顶点不在已有顶点列表中则保存该顶点
			}
		}
	}
	//归一化法线
	for (auto i : normalSet)
	{
		Normalize(vertexs[i].normal);
		//auto &n = glm::normalize(glm::make_vec3(vertexs[i].normal));
	}
	int stride = sizeof(BWeightVertexData);
	VertexAttribPointerParam params[] = {
		{ 0, 3, GL_FLOAT, false, stride, 0 },
		{ 1, 3, GL_FLOAT, false, stride, (const GLvoid*)offsetof(BWeightVertexData, normal) },
		{ 2, 2, GL_FLOAT, false, stride, (const GLvoid*)offsetof(BWeightVertexData, texcoord) },
		{ 3, 4, GL_UNSIGNED_BYTE, false, stride, (const GLvoid*)offsetof(BWeightVertexData, boneIndex) },
		{ 4, 4, GL_FLOAT, false, stride, (const GLvoid*)offsetof(BWeightVertexData, boneWeight) }
	};
	m_vaoMesh.GenVaoVbo(&vertexs[0], vertexs.size(), sizeof(vertexs[0]), params, sizeof(params) / sizeof(params[0]), &indices[0], indices.size(), GL_UNSIGNED_INT);
}

bool Model::LoadSerialName(Model *mdls, int mdlsCount, char *fmtPath, int baseNum, TextureManager *texMgr)
{
	char buf[MAX_PATH];
	for (int i = 0; i < mdlsCount; i++)
	{
		sprintf(buf, fmtPath, baseNum + i);
		if (!mdls[i].Load(buf, texMgr)) return false;
	}
	return true;
}